#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <unordered_map>
#include <cassert>
#include "Sock.hpp"
#include "Epoller.hpp"
#include "Log.hpp"
#include "Err.hpp"
#include "Util.hpp"
#include "Protocol.hpp"

namespace tcpserver
{
    class Connection;
    class TcpServer;

    static const uint16_t defaultport = 8080;
    static const int defaultfd = -1;

    static const int revs_maxnum = 64;
    static const int timeout = -1;

    using func_t = std::function<void(Connection *)>;

    // 管理每一个连接，每一个连接都要有自己的暂存读写缓冲区，把它当成一个大号结构体来用
    // 因为不能保证本轮数据读完就能读到一个完整的请求，所以读到的内容要先放到自己的读缓冲区中
    // 写缓冲区也是如此，你怎么知道你写到哪个地方呢？
    // 未来服务器会有大量的连接到来，那需不需要把这些连接管理起来呢？要的！
    // 怎么管？先描述，在组织
    // 怎么描述？下面的Connection已经描述好了
    // 怎么组织？用unordered_map管理起来
    class Connection
    {
    public:
        Connection(int sockfd, TcpServer *tsp)
            : _sockfd(sockfd), _tsp(tsp)
        {
        }

        void Register(func_t r, func_t s, func_t e)
        {
            _recver = r;
            _sender = s;
            _excepter = e;
        }

        void CloseFd()
        {
            if (_sockfd != defaultfd)
                close(_sockfd);
        }

        ~Connection()
        {
        }

    public:
        int _sockfd;

        std::string _inbuffer;  // 输入缓冲区
        std::string _outbuffer; // 输出缓冲区

        // 设置读，写， 处理异常方法
        func_t _recver;   // 从_sockfd读
        func_t _sender;   // 向_sockfd写
        func_t _excepter; // 处理IO的异常事件

        TcpServer *_tsp;
    };

    class TcpServer
    {
    public:
        TcpServer(const func_t func, const uint16_t port = defaultport, const int &num = revs_maxnum)
            : _service(func), _port(port), _revs(nullptr), _num(num)
        {
        }

        void InitServer()
        {
            // 创建socket
            _sock.Socket();
            _sock.Bind(_port);
            _sock.Listen();

            // 为底层就绪队列开辟空间
            _revs = new struct epoll_event[_num];

            // 构建epoll模型
            _epoller.Create();

            // 截至目前唯一的listensock也是一个connection，所以要管理起来
            // 由于这个连接是listensock的，所以我们只需要设置"读"回调方法，也就是Accept
            AddConnection(_sock.Getfd(), EPOLLIN | EPOLLET, std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
        }

        void Dispatcher()
        {
            while (true)
            {
                Loop();
            }
        }

        void EnableReadWrite(Connection *conn, bool read, bool write)
        {
            uint32_t events = (read ? EPOLLIN : 0) | (write ? EPOLLOUT : 0) | EPOLLET;
            _epoller.Control(conn->_sockfd, events, EPOLL_CTL_MOD);
        }

        ~TcpServer()
        {
            _sock.CloseFd();
            _epoller.CloseFd();
            if (_revs)
                delete[] _revs;
        }

    private:
        // 添加连接，这时候需要告诉我你这个connection想要在未来时间就绪时执行什么方法
        // 前两个参数相当于告诉epoll帮我关心哪个fd的哪些events
        // 后三个参数相当于epoll通知我们事件就绪时这个connection应该执行哪些方法
        void AddConnection(int sockfd, const uint32_t events, func_t recver, func_t sender, func_t excepter)
        {
            // 1. 为每一个sockfd创建Connection对象，并初始化
            Connection *conn = new Connection(sockfd, this);
            // 给fd设置对应的回调方法
            conn->Register(recver, sender, excepter);

            // 2. 将这个sockfd和它要关心的事件"写透式"地添加到epoll模型当中
            // 2.1 如果epoll的工作模式是ET的话，要先设置对应的fd为非阻塞
            if (events & EPOLLET) // 这里判断再严谨一点，设置了ET模式的fd才将它设成非阻塞
                Util::setNonBlock(sockfd);
            // 2.2 添加到epoll模型当中
            bool ret = _epoller.AddEvent(sockfd, events);
            assert(ret);
            (void)ret;

            // 3. 将connection对象添加到服务器管理的_connections当中管理起来
            _connections.insert(std::pair<int, Connection *>(sockfd, conn));

            logMessage(DEBUG, "get a new sock %d in epoller and unordered_map", sockfd);
        }

        void Accepter(Connection *conn)
        {
            // 注意，今天是ET模式下工作，所以底层连接事件就绪，epoll只会提醒一次，所以我们要打循环一直获取连接
            // 下面的DEBUG也可以证明有链接到来时，这个函数只被执行了一次，所以我们就得在这一次之内把底层的就绪连接全部一次性accept了
            logMessage(DEBUG, "accept enter!");

            for (;;)
            {
                std::string clientip;
                uint16_t clientport;

                int err = 0;
                int sock = _sock.Accept(&clientip, &clientport, &err);

                if (sock > 0)
                {
                    // 获取到一个新连接
                    // logMessage(DEBUG, "get a new link! [%s:%d]", clientip.c_str(), clientport);

                    // 添加到epoller模型和_connections管理连接的数据结构中
                    AddConnection(
                        sock, EPOLLIN | EPOLLET,
                        std::bind(&TcpServer::Recver, this, std::placeholders::_1),
                        std::bind(&TcpServer::Sender, this, std::placeholders::_1),
                        std::bind(&TcpServer::Excepter, this, std::placeholders::_1));
                }
                else
                {
                    if (err == EAGAIN || err == EWOULDBLOCK)
                        break; // 底层没有连接可以获取了
                    else if (err == EINTR)
                        continue; // 被信号中断，继续获取
                    else
                        break; // 真正获取错误
                }
            }
        }

        void Recver(Connection *conn)
        {
            // 今天我们的读取是基于ET模式下的，所以我们得一次性读完本轮数据

            while (true)
            {
                char buffer[1024];
                ssize_t s = recv(conn->_sockfd, buffer, sizeof(buffer) - 1, 0); // 这里的读取不会被阻塞的

                if (s > 0)
                {
                    // 读到数据，但可能没读完
                    buffer[s] = 0;
                    conn->_inbuffer += buffer; // 读到的数据入队列(暂存)
                    // conn->_inbuffer.pop_back();
                }
                else if (s == 0)
                {
                    // 对方把文件描述关了，所以我们执行异常处理事件
                    if (conn->_excepter)
                    {
                        conn->_excepter(conn);
                        return; // 注意，处理完就return了，不然会继续循环就出错了
                    }
                }
                else
                {
                    // 读取错误，还是跟accept一样三种情况
                    if (errno == EAGAIN || errno == EWOULDBLOCK)
                        break;
                    else if (errno == EINTR)
                        continue;
                    else
                    {
                        // 真正的异常错误
                        if (conn->_excepter)
                        {
                            conn->_excepter(conn);
                            return; // 注意，处理完就return了，不然会继续循环就出错了
                        }
                    }
                }
            }

            // logMessage(DEBUG, "%d -> %s", conn->_sockfd, conn->_inbuffer.c_str());
            _service(conn);
        }

        void Sender(Connection *conn)
        {
            // 发送也是循环发，看能不能在一轮发送中就可以把conn的outbuffer清空
            while (true)
            {
                ssize_t s = send(conn->_sockfd, conn->_outbuffer.c_str(), conn->_outbuffer.size(), 0);
                        
                conn->_outbuffer.erase(0, s);   // 清空掉已经发送的数据
                
                if (s > 0)
                {
                    // 发送成功
                    if (conn->_outbuffer.empty()) // 发送缓冲区已经空了，说明都发完了
                    {
                        // 如果发完了，不管三七二十一，不管之前有没有关心过读事件，都可以对读事件取消关心
                        // 因为进来这里，有可能是第一次没发完，第二次发完了，进到这里，所以发完了就要取消对读事件的关心
                        logMessage(DEBUG, "发完了, 发了: %d个字节", s);
                        EnableReadWrite(conn, true, false);
                        break;
                    }
                    else
                    {
                        logMessage(DEBUG, "没发完, 等下次吧!只发了: %d个字节", s);
                        logMessage(DEBUG, "conn->_outbuffer: %d Bytes", conn->_outbuffer.size());

                        break; // 注意，没写完也要break了，不然还会进行循环
                    }
                }
                else
                {
                    // 发送出错
                    if (errno == EAGAIN || errno == EWOULDBLOCK) // 底层发送缓冲区满了，下次再发了
                        break;
                    else if (errno == EINTR)
                        continue; // 被信号中断，继续发
                    else          // 真正出现异常错误了
                    {
                        if (conn->_excepter)
                        {
                            conn->_excepter(conn);
                            return; // 注意，处理完异常之后就得return了，不然就会再次循环就出错了
                        }
                    }
                }
            }

            // 走到这，如果没有发完，就需要开启对应的sockfd的读事件的关心，写在这个地方的话就不用tsp这个回指指针了
            if (!conn->_outbuffer.empty())
                EnableReadWrite(conn, true, true); // 开启对读事件的关心
            else
                EnableReadWrite(conn, true, false);
        }

        void Excepter(Connection *conn)
        {
            logMessage(DEBUG, "Excepter begin!");

            // 1. 先从epoll模型中移除(注意，要先移除，再关闭fd)
            _epoller.Control(conn->_sockfd, 0, EPOLL_CTL_DEL);

            // 2. 再从_connections移除对应的conn
            _connections.erase(conn->_sockfd);

            // 3. 再关闭conn的fd
            conn->CloseFd();

            delete conn;

            logMessage(DEBUG, "Excepter end!");
        }

        bool IsConnectionsExist(int sock)
        {
            auto iter = _connections.find(sock);
            return iter != _connections.end();
        }

        void Loop()
        {
            // 获取就绪的事件
            int n = _epoller.Wait(_revs, _num, timeout);

            for (int i = 0; i < n; i++)
            {
                int sock = _revs[i].data.fd;
                uint32_t events = _revs[i].events;

                // 如果是异常事件(对方把fd关了，或者其他问题)，我们都转为读写事件
                if (events & EPOLLERR)
                    events |= (EPOLLIN | EPOLLOUT);
                if (events & EPOLLHUP)
                    events |= (EPOLLIN | EPOLLOUT);

                // listen事件和普通读事件都可以写在一起，因为里面的读方法都是被分别绑定好的
                // 这里还需要判断当前sock在不在_connections中，因为有可能出现异常，下面第二个if判断就有可能出错
                // 还需要判断读写方法是否之前已经被注册过了，严谨一点多检查
                if ((events & EPOLLIN) && IsConnectionsExist(sock) && _connections[sock]->_recver)
                    _connections[sock]->_recver(_connections[sock]);
                if ((events & EPOLLOUT) && IsConnectionsExist(sock) && _connections[sock]->_sender)
                    _connections[sock]->_sender(_connections[sock]);
            }
        }

    private:
        uint16_t _port;
        Sock _sock;
        Epoller _epoller;
        std::unordered_map<int, Connection *> _connections;
        struct epoll_event *_revs; // ready events
        int _num;
        func_t _service;
    };

} // namespace tcpserver